package model;

import java.util.ArrayList;

import com.sampullara.poker.Board;
import com.sampullara.poker.Deck;
import com.sampullara.poker.HandRank;
import com.sampullara.poker.HandRank.Rank;

public class Game {
	
	private String name;
	private ArrayList<Player> players;
	private Deck deck;
	private int blind;
	private int pot;
	private boolean started = false;
	private String state = null;
	
	private ArrayList<Player> playersInCurrentGame;
	private int highestBet;
	private int raiseAmount;
	private Player raisePlayer;
	private int playersTurn = 0;
    private Board board;
    private Rank winningRank;
    boolean waitForNewPlayer;
	
    //Initializeren van de huidige table
	public Game(String name, Player player, int blind) {
		waitForNewPlayer = false;
		players = new ArrayList<Player>();
		playersInCurrentGame = new ArrayList<Player>();
		
		players.add(player);
		this.name = name;
		this.blind = blind;
		this.state = "waiting";
	}
	
	public boolean isStarted() {
		return started;
	}
	
	public String getState() {
		return state;
	}
	
	//Indien iemand het spel verlaat
	public void leaveGame(Player player) {
		waitForNewPlayer = true;
		int leavingPlayerIndex = -1;
		for(Player p : playersInCurrentGame) {
			if(player.getName().equals(p.getName())) {
				leavingPlayerIndex = players.indexOf(p);
			}
		}
		if(playersTurn == leavingPlayerIndex) {
			if(playersTurn == playersInCurrentGame.size()-1) {
				playersTurn = 0;
			}
			else {
				playersTurn++;
			}
		}
		playersInCurrentGame.remove(leavingPlayerIndex);
		players.remove(player);
	}
	
	//Volgorde van spelers aanpassen, als het niet de eerste ronde is.
	public void newGame() {
		System.out.println("newGame() aangeroepen");
		ArrayList<Player> temp = new ArrayList<Player>();
		temp.add(players.get(1));
		temp.add(players.get(2));
		temp.add(players.get(3));
		temp.add(players.get(0));
		players.clear();
		for(Player p : temp) {
			players.add(p);
		}
		startGame();
	}
	
	//Nieuwe ronde starten.
	public void startGame() {
		started = true;
		if (players.size() > 1) {
			board = new Board();
			highestBet = 0;
			raiseAmount = 0;
			raisePlayer = null;
			deck = new Deck();
			deck.shuffle();
			
			pot = 0;
			state = "started";

			playersInCurrentGame.clear();
			for(Player p : players) {
				p.resetCards();
				p.setFolded(false);
				playersInCurrentGame.add(p);
			}
			playersInCurrentGame.get(2).setCurrentBet(blind);
			playersInCurrentGame.get(3).setCurrentBet(2*blind);
			pot += 3*blind;
			highestBet = 2*blind;
			playersTurn = 0;
			preFlop();
		}
	}
	
	//Voor het geval iemand geleaved is en huidige ronde afgelopen is. Eerst wachten op nieuwe speler.
	public void waitForNewPlayer() {
		started = false;
	}
	
	public void preFlop() {
		for (int i = 0; i < 2; i++) {
			for (Player p : players) {
				p.giveCard(deck.deal());
			}
		}
		state = "preflop";
	}
	
	public void flop() {
		if (board.getCards().isEmpty()) {
			deck.burn();
			for (int i = 0; i < 3; i++) {
				board.addCard(deck.deal());
			}
			board.addCard(deck.deal());
			state = "flop";
		} else {
			board = new Board();
			flop();
		}
	}

	public void turn() {
		deck.burn();
		board.addCard(deck.deal());
		state = "turn";
	}

	public void river() {
		deck.burn();
		board.addCard(deck.deal());
		state = "river";
	}
	
	//Zodra de ronde afgelopen is, controleren wie er gewonnen heeft, en nieuwe ronde starten.
	public void finishGame() {
        resolve();
        for(Player player : playersInCurrentGame) {
            if(player.getHandRank(board).getRank() == winningRank) {
                player.setChips(player.getChips() + pot);
            }
        }
		
        if(waitForNewPlayer) {
			waitForNewPlayer();
		}
        else {
        	System.out.println("newGame aangeroepen uit finishGame()");
        	newGame();
        }
	}
	
	//Actie: raise
	public void raise(Player player, int raise) {
		if(highestBet != 0) {
			if(raise >= (highestBet*2)) {
				if(raisePlayer != player) {
					if(new PlayerDao().getChips(player.getName()) >= raise) {
						raiseAmount = raise;
						raisePlayer = player;
						player.setChips(player.getChips() - raise);
						player.setCurrentBet(raise);
						pot += raise;
						if(playersTurn == playersInCurrentGame.size()-1) {
							playersTurn = 0;
						}
						else {
							playersTurn++;
						}
					}
				}
			}
		}
	}
	
	//Actie: bet
	public void bet(Player player, int bet) {
		if((raiseAmount > 0) || (highestBet > 0)) { }
		else {
			if(new PlayerDao().getChips(player.getName()) >= bet) {
			
				highestBet = bet;
				player.setChips(player.getChips() - bet);
				player.setCurrentBet(bet);
				pot += bet;
				if(playersTurn == playersInCurrentGame.size()-1) {
					playersTurn = 0;
				}
				else {
					playersTurn++;
				}
			}
			else { System.out.println("Niet genoeg chips"); }
		}
	}
	
	//Actie: call
	public void call(Player player) {
		if(highestBet == 0) {
		}
		else if(new PlayerDao().getChips(player.getName()) < highestBet) {
		}
		else {
			if(player.getCurrentBet() != 0) {
				player.setChips(player.getChips() - (highestBet - player.getCurrentBet()));
				pot += (highestBet - player.getCurrentBet());
				player.setCurrentBet(highestBet);
			}
			else {
				player.setChips(player.getChips() - highestBet);
				pot += highestBet;
				player.setCurrentBet(highestBet);
			}
			if(playersTurn == playersInCurrentGame.size()-1) {
				playersTurn = 0;
				
				if(raiseAmount == 0) {
					if(playersInCurrentGame.get(playersTurn).getCurrentBet() == highestBet) {
						nextTurn();
					}
					else {
						
					}
				}
			}
			
			else {
				playersTurn++;
				if(raiseAmount == 0) {
					if(playersInCurrentGame.get(playersTurn).getCurrentBet() == highestBet) {
						playersTurn = 0;
						nextTurn();
					}
				}
			}
		}
	}
	
	//Actie: check
	public void check() {
		if(!(highestBet > 0)) {
			if(playersTurn == playersInCurrentGame.size()-1) {
				playersTurn = 0;
				nextTurn();
			}
			else {
				playersTurn++;
			}
		}
		else {
			System.out.println("Er is een 'bet' gedaan. Check is niet mogelijk.");
		}
	}
	
	//Actie: fold
	public void fold(Player player) {
		int foldingPlayer = -1;
		for(Player p : playersInCurrentGame) {
			if(p.getName().equals(player.getName())) {
				p.setFolded(true);
				foldingPlayer = playersInCurrentGame.indexOf(p);
			}
		}
		playersInCurrentGame.remove(foldingPlayer);
		if(playersTurn == (playersInCurrentGame.size())) {
			playersTurn = 0;
		}
		
		if(highestBet != 0) {
			if(raiseAmount == 0) {
				if(playersInCurrentGame.get(playersTurn).getCurrentBet() == highestBet) {
					nextTurn();
				}
			}
		}

		if(playersInCurrentGame.size() == 1) {
	    	//Laatste speler die over is heeft gewonnen.
	        playersInCurrentGame.get(0).setChips(playersInCurrentGame.get(0).getChips() + pot);
	        resetBets();
	        //TODO: Laten zien we er gewonnen heeft.
	        newGame();
	    }
	}
	
	//Volgende game-state (schakelt tussen preflop, flop, turn, river en showDown).
	public void nextTurn() {
		resetBets();
		if(state.equals("river")) {
			state = "showDown";
			finishGame();
		}
		else if(state.equals("turn")) {
			river();
			state = "river";
		}
		else if(state.equals("flop")) {
			turn();
			state = "turn";
		}
		else if(state.equals("preflop")) {
			flop();
			state = "flop";
		}
	}
	
	//Alle bets van de afgelopen ronde resetten.
	public void resetBets() {
		for(Player p : players) {
			p.setCurrentBet(0);
			highestBet = 0;
			raiseAmount = 0;
			raisePlayer = null;
		}
	}
	
	//Nieuwe speler toevoegen aan spel.
	public void newPlayer(Player player) {
		players.add(player);
	}
	
	public String getName() {
		return name;
	}

	public ArrayList<Player> getPlayers() {
		return players;
	}

    public ArrayList<Player> getActivePlayers(){
        return playersInCurrentGame;
    }

	public int getBlind() {
		return blind;
	}
	
	public int getPot() {
		return pot;
	}
	
	public void setPot(int pot) {
		this.pot = pot;
	}

    public Board getBoard(){
        return board;
    }
    
    public int getHighestBet() {
    	return highestBet;
    }
    
    public void setHighestBet(int bet) {
    	highestBet = bet;
    }
    
    //Vraag speler op met bepaald sessionID.
    public Player getPlayer(String session){
        for(Player player: playersInCurrentGame){
            if(player.getSession().equals(session)){
                return player;
            }
        }
        return null;
        
    }

    //Vraag de speler op die momenteel aan de beurt is.
    public Player getActivePlayer(){
        return playersInCurrentGame.get(playersTurn);
    }

     public void resolve() {
    	 //TODO: Nodig?
        HandRank winningHandRank = null;
        for (Player player : playersInCurrentGame) {
        	System.out.println("Handrank " + player.getName() + ": " + player.getHandRank(board));
            HandRank rank = player.getHandRank(board);
            if (winningHandRank == null) {
                winningHandRank = rank;
            } else {
                int compare = rank.compareTo(winningHandRank);
                if (compare > 0) {
                    winningHandRank = rank;
                }
            }
        }
        winningRank = winningHandRank.getRank();
    }

}